import os
import subprocess
import argparse
import re
import json
import tempfile
import shutil
import math
from datetime import datetime

def parse_time(time_str):
    """将时间字符串转换为秒数"""
    try:
        if '.' in time_str:
            time_obj = datetime.strptime(time_str, "%H:%M:%S.%f")
        else:
            time_obj = datetime.strptime(time_str, "%H:%M:%S")
        return time_obj.hour * 3600 + time_obj.minute * 60 + time_obj.second + time_obj.microsecond / 1000000
    except ValueError:
        try:
            return float(time_str)
        except:
            return 0

def read_silence_intervals(file_path):
    """读取静音区间文件"""
    intervals = []
    total_duration = 0
    silence_percent = 0
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()
            
            for line in lines:
                if line.startswith('开始时间') or line.strip() == '':
                    continue
                
                if '总静音时间' in line:
                    match = re.search(r'总静音时间: (\d+\.?\d*)秒', line)
                    if match:
                        total_duration = float(match.group(1))
                    match = re.search(r'\((\d+\.?\d*)%\)', line)
                    if match:
                        silence_percent = float(match.group(1))
                    continue
                
                parts = re.split(r'\s+', line.strip())
                if len(parts) >= 2:
                    start = parse_time(parts[0])
                    end = parse_time(parts[1])
                    if start < end : 
                        intervals.append((start, end))
    
    except Exception as e:
        print(f"读取静音区间文件出错: {str(e)}")

    # 按开始时间排序
    intervals.sort(key=lambda x: x[0])

    # 如果 上一个 结束时间 大于等于 下一个 开始时间，则合并
    if intervals:
        merged_intervals = []
        current_start, current_end = intervals[0]
        
        for start, end in intervals[1:]:
            if start <= current_end + 1:  # 允许1秒的间隙, 进行合并
                current_end = max(current_end, end)
            else:
                merged_intervals.append((current_start, current_end))
                current_start, current_end = start, end
        
        merged_intervals.append((current_start, current_end))
        intervals = merged_intervals 
    
    return intervals, total_duration, silence_percent

def find_keyframe_near_time(input_file, time_point):
    """在指定时间点附近查找最近的关键帧"""
    try:
        cmd = [
            'ffprobe',
            '-v', 'error',
            '-skip_frame', 'nokey',
            '-select_streams', 'v:0',
            '-show_entries', 'frame=best_effort_timestamp_time',
            '-of', 'csv=p=0',
            '-read_intervals', f'{max(0, time_point-1)}%{time_point+1}',
            input_file
        ]
        
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        keyframes = [float(line.strip()) for line in result.stdout.splitlines() if line.strip()]
        
        if not keyframes:
            return time_point
            
        # 找到最接近指定时间的关键帧
        closest = min(keyframes, key=lambda x: abs(x - time_point))
        return closest
        
    except Exception:
        return time_point

def get_video_duration(input_file):
    """获取视频总时长（秒）"""
    try:
        cmd = [
            'ffprobe',
            '-v', 'error',
            '-show_entries', 'format=duration',
            '-of', 'json',
            input_file
        ]
        
        result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        data = json.loads(result.stdout)
        return float(data['format']['duration'])
    except Exception:
        return 0

def create_keep_intervals(silence_intervals, total_duration):
    """根据静音区间创建需要保留的区间"""
    keep_intervals = []
    
    if not silence_intervals:
        return [(0, total_duration)]
    
    silence_intervals.sort(key=lambda x: x[0])
    
    # 第一个保留区间
    if silence_intervals[0][0] > 0:
        keep_intervals.append((0, silence_intervals[0][0]))
    
    # 中间保留区间
    for i in range(len(silence_intervals) - 1):
        end_current = silence_intervals[i][1]
        start_next = silence_intervals[i+1][0]
        
        if end_current < start_next:
            keep_intervals.append((end_current, start_next))
    
    # 最后一个保留区间
    if silence_intervals[-1][1] < total_duration:
        keep_intervals.append((silence_intervals[-1][1], total_duration))
    
    # 合并相邻区间
    merged_intervals = []
    current_start, current_end = keep_intervals[0]
    
    for start, end in keep_intervals[1:]:
        if start <= current_end + 0.1:  # 允许0.1秒的间隙
            current_end = max(current_end, end)
        else:
            merged_intervals.append((current_start, current_end))
            current_start, current_end = start, end
    
    merged_intervals.append((current_start, current_end))
    
    return merged_intervals

def create_segment(input_file, start, end, output_file):
    """创建单个视频片段"""
    cmd = [
        'ffmpeg',
        '-ss', str(start),
        '-to', str(end),
        '-i', input_file,
        '-c', 'copy',  # 复制流，不重新编码
        '-y',
        output_file
    ]
    
    try:
        subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return True
    except subprocess.CalledProcessError as e:
        print(f"创建片段失败: {e.stderr.decode()}")
        return False

def concatenate_videos(input_files, output_file):
    """连接多个视频文件"""
    # 创建文件列表
    list_file = tempfile.mktemp(suffix='.txt')
    
    with open(list_file, 'w', encoding='utf-8') as f:
        for file in input_files:
            f.write(f"file '{file}'\n")
    
    cmd = [
        'ffmpeg',
        '-f', 'concat',
        '-safe', '0',
        '-i', list_file,
        '-c', 'copy',  # 复制流，不重新编码
        '-y',
        output_file
    ]
    
    try:
        result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        return True
    except subprocess.CalledProcessError as e:
        print(f"连接视频失败: {e.stderr.decode()}")
        return False
    finally:
        # 清理临时文件
        if os.path.exists(list_file):
            os.remove(list_file)

def cut_video_silence(input_file, output_file, silence_file, overwrite=False):
    """裁剪视频中的静音部分（分段处理）"""
    # 获取视频总时长
    total_duration = get_video_duration(input_file)
    
    # 读取静音区间
    silence_intervals, total_silence, silence_percent = read_silence_intervals(silence_file)

    # 关键帧对齐
    aligned_intervals = []
    for start, end in silence_intervals:
        aligned_start = find_keyframe_near_time(input_file, start)
        aligned_end = find_keyframe_near_time(input_file, end)
        if aligned_start < aligned_end:
            aligned_intervals.append((aligned_start, aligned_end))
    silence_intervals = aligned_intervals
    
    # 创建需要保留的区间
    keep_intervals = create_keep_intervals(silence_intervals, total_duration)
    
    if not keep_intervals:
        print("没有需要保留的区间")
        return False
    
    print(f"视频总时长: {total_duration:.2f}秒")
    print(f"检测到静音区间: {len(silence_intervals)}个")
    print(f"总静音时间: {total_silence:.2f}秒 ({silence_percent:.2f}%)")
    print(f"将保留 {len(keep_intervals)} 个非静音区间")
    
    # 创建临时目录
    temp_dir = tempfile.mkdtemp()
    segment_files = []
    
    try:
        # 创建每个保留区间的片段
        for i, (start, end) in enumerate(keep_intervals):
            segment_file = os.path.join(temp_dir, f"segment_{i:04d}.mp4")
            print(f"创建片段 {i+1}/{len(keep_intervals)}: {start:.2f}-{end:.2f}秒")
            
            if create_segment(input_file, start, end, segment_file):
                segment_files.append(segment_file)
        
        # 连接所有片段
        print(f"连接 {len(segment_files)} 个片段...")
        if concatenate_videos(segment_files, output_file):
            # 获取新视频时长
            new_duration = get_video_duration(output_file)
            print(f"裁剪成功: {input_file} -> {output_file}")
            print(f"原始时长: {total_duration:.2f}秒, 新时长: {new_duration:.2f}秒, 裁剪了 {total_duration - new_duration:.2f}秒")
            return True
        else:
            print("连接片段失败")
            return False
    except Exception as e:
        print(f"处理过程中出错: {str(e)}")
        return False
    finally:
        # 清理临时文件
        for file in segment_files:
            if os.path.exists(file):
                os.remove(file)
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)

def main():
    parser = argparse.ArgumentParser(description='裁剪视频中的静音部分')
    parser.add_argument('input', help='输入视频文件路径')
    parser.add_argument('silence_file', help='静音区间文件路径')
    parser.add_argument('-o', '--output', help='输出视频文件路径（默认为输入文件加_cut后缀）')
    parser.add_argument('-y', '--overwrite', action='store_true', help='覆盖已存在的输出文件')
    
    args = parser.parse_args()
    
    # 设置默认输出文件名
    if args.output is None:
        filename, ext = os.path.splitext(args.input)
        args.output = f"{filename}_cut{ext}"
    
    # 检查输入文件是否存在
    if not os.path.isfile(args.input):
        print(f"错误: 输入视频文件不存在 - {args.input}")
        return
    
    # 删除已存在的输出文件
    if os.path.isfile(args.output):
        if args.overwrite:
            os.remove(args.output)
        else:
            print(f"错误: 输出文件已存在 - {args.output} (使用 -y 选项覆盖)")
            return
    
    if not os.path.isfile(args.silence_file):
        print(f"错误: 静音区间文件不存在 - {args.silence_file}")
        return
    
    print(f"输入视频: {args.input}")
    print(f"静音区间文件: {args.silence_file}")
    print(f"输出视频: {args.output}")
    
    success = cut_video_silence(
        input_file=args.input,
        output_file=args.output,
        silence_file=args.silence_file,
        overwrite=args.overwrite
    )
    
    if success:
        print("视频裁剪完成!")
    else:
        print("视频裁剪失败")

if __name__ == "__main__":
    main()